// get the size of a padding string to 4-byte align the next piece of data
owl_byte owl_get_padding_align_4_length(owl_umax byte_pos)
{
  owl_umax tmp = byte_pos;
  while (byte_pos % 4 != 0) byte_pos++;
  return byte_pos - tmp;
}

// get the size of a padding string to 32-byte align the next piece of data
owl_byte owl_get_padding_align_32_length(owl_umax byte_pos)
{
  owl_umax tmp = byte_pos;
  while (byte_pos % 32 != 0) byte_pos++;
  return byte_pos - tmp;
}

// check main pointer
owl_bool owl_check_smg_name_table_main_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_smg_name_table_main));
}

// check ext pointer
owl_bool owl_check_smg_name_table_ext_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_smg_name_table_extra));
}

// check pointer
owl_bool owl_check_smg_name_table_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_smg_name_table));
}

// check main members
owl_bool owl_check_smg_name_table_main_members(owl_umax count, owl_str ** names)
{
  // check params
  if (count == 0 || count > 0xFFFF) goto err;
  for (owl_umax i = 0; i < count; i++)
    if (owl_check_str_all(names[i], sizeof(owl_str)) == owl_false
        || names[i] -> enc != OWL_WCHAR_ENC_SHIFT_JIS
        || owl_find_str_ch(names[i], OWL_SHIFT_JIS_NULL) == NULL)
      goto err;
  
  // done!
  return owl_true;
  err:
  return owl_false;
}

// check main all
owl_bool owl_check_smg_name_table_main_all(owl_smg_name_table_main * main, owl_umax size)
{
  return (owl_check_smg_name_table_main_pointer((void *) main, size) == owl_true
          && owl_check_smg_name_table_main_members(main -> count, main -> names) == owl_true);
}

// check all
owl_bool owl_check_smg_name_table_all(owl_smg_name_table * names, owl_umax size)
{
  // check params
  if (names == NULL || size < sizeof(owl_smg_name_table))
    goto err;
  if (names -> ext == NULL) // this structure section is not strictly required
    goto end;
  
  // check all data together
  for (owl_umax i = 0, pos = (4 * (names -> main -> count + 1)); i < names -> main -> count; i++) {
    ((names -> main -> names)[i] -> arr -> size)--;
    // ^ so that the owl_calc_str_schar_pol3_hash() calculates correctly
    if ((((names -> ext -> names_data)[i].hash & 0xFFFF) !=
         (owl_calc_str_schar_pol3_hash((names -> main -> names)[i]) & 0xFFFF))
        || (names -> ext -> names_data)[i].index != pos /* placed in the correct index */) goto err;
    ((names -> main -> names)[i] -> arr -> size)++;
    pos += (names -> main -> names)[i] -> arr -> size;
  }
  
  // done!
  end:
  return owl_check_smg_name_table_main_all(names -> main, sizeof(owl_smg_name_table_main));
  err:
  return owl_false;
}

// create
owl_smg_name_table * owl_create_smg_name_table(owl_str * raw, owl_endian endian)
{
  // check params
  OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_ALL_GOOD;
  OWL_SMG_NAME_TABLE_ERR_READ_POS = 0;
  if (owl_check_str_all(raw, sizeof(owl_str)) == owl_false || owl_check_endian(endian) == owl_false) {
    OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_FUNC_PARAMS;
    return NULL;
  }
  
  // variable to help read raw
  owl_bitarr bits = {&((owl_mem_space) {raw -> arr -> ptr, raw -> arr -> size}), 0, 0};
  
  // fill new while checking it
  owl_smg_name_table * new = NULL;
  owl_mem_space * tmp_sp = owl_create_mem_space(sizeof(owl_smg_name_table));
  if (tmp_sp == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_MEM_ALLOC; goto err;}
  new = (void *) tmp_sp -> ptr;
  tmp_sp = owl_create_mem_space(sizeof(owl_smg_name_table_main));
  if (tmp_sp == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_MEM_ALLOC; goto err;}
  new -> main = (void *) tmp_sp -> ptr;
  tmp_sp = owl_create_mem_space(sizeof(owl_smg_name_table_extra));
  if (tmp_sp == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_MEM_ALLOC; goto err;}
  new -> ext = (void *) tmp_sp -> ptr;
  
  // count and unknown 1
  if (bits.arr -> size < 4) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_STR_SIZE; goto err;}
  new -> main -> count = owl_get_std_uint16_mw(&bits, endian).integer.u;
  // unknown 1
  new -> ext -> unknown1 = owl_get_std_uint16_mw(&bits, endian).integer.u;
  if (new -> ext -> unknown1 != OWL_SMG_NAME_TABLE_UNKNOWN1) 
    {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_UNKNOWN1; goto err;}
  
  
  // name data
  if (bits.arr -> size < new -> main -> count * 4)
    {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_STR_SIZE; goto err;}
  tmp_sp = owl_create_mem_space(new -> main -> count * sizeof(struct owl_smg_name_table_name_data));
  if (tmp_sp == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_MEM_ALLOC; goto err;}
  new -> ext -> names_data = (void *) tmp_sp -> ptr;
  for (owl_umax i = 0; i < new -> main -> count; i++) {
    (new -> ext -> names_data)[i].hash = owl_get_std_uint16_mw(&bits, endian).integer.u; // check later
    (new -> ext -> names_data)[i].index = owl_get_std_uint16_mw(&bits, endian).integer.u; // check later
  }
    
  // names, size checking will be done while reading the strings
  tmp_sp = owl_create_mem_space(new -> main -> count * sizeof(owl_str *));
  if (tmp_sp == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_MEM_ALLOC; goto err;}
  new -> main -> names = (void *) tmp_sp -> ptr;
  for (owl_umax i = 0, pos = (4 * (new -> main -> count + 1)); i < new -> main -> count; i++) {
    if (bits.arr -> size == 0) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_STR_SIZE; goto err;}
    owl_str * str = owl_get_str_until_ch(bits.arr -> ptr, bits.arr -> size,
                                         OWL_WCHAR_ENC_SHIFT_JIS, OWL_SHIFT_JIS_NULL, owl_true);
    if (str == NULL) {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_NAME; goto err;}
    (str -> arr -> size)--; // so that the owl_calc_str_schar_pol3_hash() calculates correctly the hash
    if ((new -> ext -> names_data)[i].index != pos
        || (new -> ext -> names_data)[i].hash != (owl_calc_str_schar_pol3_hash(str) & 0xFFFF))
      {OWL_SMG_NAME_TABLE_ERR_CD = OWL_SMG_NAME_TABLE_ERR_NAME_HASH_INDEX; goto err_str;}
    (str -> arr -> size)++;
    (new -> main -> names)[i] = str;
    bits.arr -> ptr += str -> arr -> size;
    bits.arr -> size -= str -> arr -> size;
    pos += str -> arr -> size;
    continue;
    
    // error when reading the string
    err_str:
    for (owl_umax j = 0; j < i; j++) owl_free_str((new -> main -> names)[j]);
    if (str != NULL) owl_free_str(str);
    goto err;
  }
  
  // done!
  return new;
  err:
  OWL_SMG_NAME_TABLE_ERR_READ_POS = bits.arr -> ptr - raw -> arr -> ptr;
  if (new != NULL) {
    // main section
    if (new -> main != NULL) {
      if (new -> main -> names != NULL) {
        for (owl_umax i = 0; i < new -> main -> count; i++)
          owl_free_str((new -> main -> names)[i]);
        owl_free_mem_space(&((owl_mem_space) {(void *) new -> main -> names, 1}));
      }
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> main, 1}));
    }
    // extra section
    if (new -> ext != NULL) {
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> ext -> names_data, 1}));
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> ext, 1}));
    }
  }
  owl_free_mem_space(&((owl_mem_space) {(void *) new, 1}));
  owl_free_mem_space(tmp_sp);
  return NULL;
}

// free
owl_umax owl_free_smg_name_table(owl_smg_name_table * names)
{
  // check params
  if (owl_check_smg_name_table_all(names, sizeof(owl_smg_name_table)) == owl_false) goto err;
  
  // free the memory used by the structure 
  // main
  for (owl_umax i = 0; i < names -> main -> count; i++)
    owl_free_str((names -> main -> names)[i]);
  owl_free_mem_space(&((owl_mem_space) {(void *) names -> main -> names, 1}));
  owl_free_mem_space(&((owl_mem_space) {(void *) names -> main, 1}));  
  // ext
  owl_free_mem_space(&((owl_mem_space) {(void *) names -> ext -> names_data, 1}));
  owl_free_mem_space(&((owl_mem_space) {(void *) names -> ext, 1}));
  // structure itself
  return owl_free_mem_space(&((owl_mem_space) {(void *) names, 1}));
  err:
  return 0;
}

// print
owl_umax owl_print_smg_name_table(owl_byte * src, owl_umax size)
{
  // check params
  printf("OWL_SMG_NAME_TABLE struct info:\n");
  printf("  Struct info: %p, %llu\n", src, size);
  if (owl_check_smg_name_table_all((void *) src, size) == owl_false)
    return 0;
  
  // else print the structure
  owl_smg_name_table * names = (void *) src;
  printf("  OWL_SMG_NAME_TABLE_MAIN struct info:\n");
  printf("    Struct info: %p, %lu\n", names -> main, sizeof(owl_smg_name_table_main));
  printf("    Number of names: %llu\n", names -> main -> count);
  printf("    Names (%s): length, string\n", OWL_WCHAR_ENC_STR[(names -> main -> names)[0] -> enc]);
  for (owl_umax i = 0; i < (names -> main -> count); i++) {
    printf("      Name[%llu]: %llu, ", i, (names -> main -> names)[i] -> arr -> size);
    for (owl_umax j = 0; j < (names -> main -> names)[i] -> arr -> size; putchar(' ')) {
      j += (names -> main -> names)[i] -> print_wchar_enc((names -> main -> names)[i] -> arr -> ptr + j,
                                                          (names -> main -> names)[i] -> arr -> size);
    }
    putchar('\n');
  }
  printf("  OWL_SMG_NAME_TABLE_EXT struct info:\n");
  printf("    Struct info: %p, %lu\n", names -> ext, sizeof(owl_smg_name_table_extra));
  printf("    Unknown 1: %02llX\n", names -> ext -> unknown1);
  printf("    Names data: hash, index\n");
  for (owl_umax i = 0; i < (names -> main -> count); i++) {
    printf("      Name[%llu]: %04llX, %llX\n",
           i, (names -> ext -> names_data)[i].hash,
           (names -> ext -> names_data)[i].index);
  }
  return sizeof(owl_smg_name_table);
}

// compare
owl_char owl_comp_smg_name_table(owl_byte * names1, owl_byte * names2)
{
  // later
  return owl_comp_err;
}

// get the extra information of a smg name table
owl_bool owl_get_smg_name_table_extra(owl_smg_name_table * names)
{
  // check params
  if (owl_check_smg_name_table_all(names, sizeof(owl_smg_name_table)) == owl_false
      || names -> ext != NULL /* careful now */)
    return owl_false;
  
  // allocate space
  owl_smg_name_table_extra * ext = NULL;
  owl_mem_space * sp = owl_create_mem_space(sizeof(owl_smg_name_table_extra));
  if (sp == NULL) goto err;
  ext = (void *) sp -> ptr;
  sp = owl_create_mem_space(names -> main -> count * sizeof(struct owl_smg_name_table_name_data));
  if (sp == NULL) goto err;
  ext -> names_data = (void *) sp -> ptr;
  
  // unknown 1, names indexes and hashes
  ext -> unknown1 = OWL_SMG_NAME_TABLE_UNKNOWN1;
  for (owl_umax i = 0, pos = (4 * (names -> main -> count + 1)); i < names -> main -> count; i++) {
    (ext -> names_data)[i].index = pos;
    ((names -> main -> names)[i] -> arr -> size)--;
    (ext -> names_data)[i].hash = owl_calc_str_schar_pol3_hash((names -> main -> names)[i]);
    pos += ++((names -> main -> names)[i] -> arr -> size);
  }
    
  // done!
  names -> ext = ext;
  return owl_true;
  err:
  if (ext != NULL)
    owl_free_mem_space(&((owl_mem_space) {(void *) ext -> names_data, 1}));
  owl_free_mem_space(&((owl_mem_space) {(void *) ext, 1}));
  owl_free_mem_space(sp);
  return owl_false;
}

// get the binary size of a name table
owl_umax owl_get_smg_name_table_bin_size(owl_smg_name_table * names)
{
  // check params
  if (owl_check_smg_name_table_all(names, sizeof(owl_smg_name_table)) == owl_false)
    return 0;
  
  // calculate the size
  owl_umax size = 4 * (names -> main -> count + 1);
  for (owl_umax i = 0; i < names -> main -> count; i++)
    size += (names -> main -> names)[i] -> arr -> size;
  
  // done!
  return size;
}

// get the error code of a name table string
owl_smg_name_table_err_cd owl_get_err_cd_smg_name_table(owl_str * raw, owl_endian endian)
{
  // reuse owl_create_smg_name_table()
  owl_smg_name_table * tmp = owl_create_smg_name_table(raw, endian);
  owl_free_smg_name_table(tmp);
  return OWL_SMG_NAME_TABLE_ERR_CD;
}

// get a name table from minimal args
owl_smg_name_table * owl_get_smg_name_table_min_args(owl_umax count, owl_str ** names)
{
  // check params
  if (owl_check_smg_name_table_main_all(&((owl_smg_name_table_main) {count, names}),
                                        sizeof(owl_smg_name_table_main)) == owl_false)
    return NULL;
  
  // return the structure
  owl_smg_name_table * new = NULL;
  owl_mem_space * sp = owl_create_mem_space(sizeof(owl_smg_name_table));
  if (sp == NULL) goto err;
  new = (void *) sp -> ptr;
  sp = owl_create_mem_space(sizeof(owl_smg_name_table_main));
  if (sp == NULL) goto err;
  new -> main = (void *) sp -> ptr;
  
  // assign the variables
  new -> main -> count = count;
  sp = owl_create_mem_space(count * sizeof(owl_str *));
  if (sp == NULL) goto err;
  new -> main -> names = (void *) sp -> ptr;
  for (owl_umax i = 0; i < count; i++)
    (new -> main -> names)[i] = owl_get_str_until_ch(names[i] -> arr -> ptr,
                                                     names[i] -> arr -> size,
                                                     OWL_WCHAR_ENC_SHIFT_JIS,
                                                     OWL_SHIFT_JIS_NULL,
                                                     owl_true);
  if (owl_get_smg_name_table_extra(new) == owl_false) goto err;
  
  // done!
  return new;
  err:
  if (new != NULL) {
    // main section
    if (new -> main != NULL) {
      if (new -> main -> names != NULL) {
        for (owl_umax i = 0; i < new -> main -> count; i++)
          owl_free_str((new -> main -> names)[i]);
        owl_free_mem_space(&((owl_mem_space) {(void *) new -> main -> names, 1}));
      }
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> main, 1}));
    }
    // extra section
    if (new -> ext != NULL) {
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> ext -> names_data, 1}));
      owl_free_mem_space(&((owl_mem_space) {(void *) new -> ext, 1}));
    }
  }
  owl_free_mem_space(&((owl_mem_space) {(void *) new, 1}));
  owl_free_mem_space(sp);
  return NULL;
}

// encode a smg_name_table struct
owl_str * owl_get_smg_name_table_as_str(owl_smg_name_table * names, owl_endian endian)
{
  // check params
  owl_mem_space * sp = NULL;
  if (owl_check_smg_name_table_all(names, sizeof(owl_smg_name_table)) == owl_false
      || owl_check_endian(endian) == owl_false)
    goto err;
  
  // allocate the space needed
  sp = owl_create_mem_space(owl_get_smg_name_table_bin_size(names));
  if (sp == NULL) goto err;
  
  // start writing shidge
  owl_bitarr write = {&((owl_bytarr) {sp -> ptr, sp -> size}), 0, 0};
  
  // count and unknown 1
  if (owl_write_std_uint16_mv(names -> main -> count, &write, endian) == owl_false) goto err;
  if (owl_write_std_uint16_mv(names -> ext -> unknown1, &write, endian) == owl_false) goto err;
  
  // names data
  for (owl_umax i = 0; i < names -> main -> count; i++) {
    if (owl_write_std_uint16_mv((names -> ext -> names_data)[i].hash, &write, endian) == owl_false) goto err;
    if (owl_write_std_uint16_mv((names -> ext -> names_data)[i].index, &write, endian) == owl_false) goto err;
  }
  
  // names
  for (owl_umax i = 0; i < names -> main -> count; i++) {
    owl_copy_bytarr((names -> main -> names)[i] -> arr, write.arr);
    write.arr -> ptr += (names -> main -> names)[i] -> arr -> size;
    write.arr -> size -= (names -> main -> names)[i] -> arr -> size;
  }
  
  // done!
  owl_str * new = owl_create_str(sp -> ptr, sp -> size, OWL_WCHAR_ENC_BIN);
  owl_free_mem_space(sp);
  return new;
  err:
  owl_free_mem_space(sp);
  return NULL;
}
